	'
	'	Project MN1307
	'	==============
	'	Embedded Software Version 1.00
	'	Target Processor PIC16F628-04/SP
	'	Target PCB - BB-01
	'	Created 12/06/02  
	'	Last Updated 12/06/02
	'	Written by Melanie Newman
	'
	'	DS1307 RTC Program
	'	Includes Setting the Date and Time
	'	   3 Buttons Control Setting...
	'		12/24 hour Clock Mode
	'		Year
	'		Month
	'		Day of Month (also accounting for Leap Years)
	'		Hour
	'		Minute
	'		Second
	'
	'	Written for 2 line 16 Character LCD
	'
	'	This program is designed to "hold your hand" and prevent you
	'	   from setting any invalid data combinations into the DS1307
	'
	'	It's a crude program, but demonstrates how to set the clock
	'	   with control buttons
	'
	'	You will be automatically returned to the Time & Date display if
	'	   you are in the Setup and don't touch any Buttons for 20 seconds
	'

	'
	'	Device Programming Options
	'	--------------------------
	@ DEVICE pic16F628, INTRC_OSC_NOCLKOUT
			' System Clock Options	
	@ DEVICE pic16F628, WDT_ON
			' Watchdog Timer
	@ DEVICE pic16F628, PWRT_ON
			' Power-On Timer
	@ DEVICE pic16F628, BOD_ON
			' Brown-Out Detect
	@ DEVICE pic16F628, MCLR_OFF
			' Master Clear Options (Internal)
	@ DEVICE pic16F628, LVP_OFF
			' Low-Voltage Programming
	@ DEVICE pic16F628, CPD_OFF
			' Data Memory Code Protect
	@ DEVICE pic16F628, PROTECT_OFF
			' Program Code Protection

	'
	'	Hardware Assignments
	'	--------------------
		'
		'	System LCD
		'	----------
		'	No Resistors required on any line
		'	RA0-RA3 Data bits DB0-DB3
		'	RA6 RS-bit
		'	RA7 E-Bit
		'
	Define LCD_DREG PORTA	' Port for LCD Data
	Define LCD_DBIT 0	' Use lower 4 bits of Port
	Define LCD_RSREG PORTA	' Port for RegisterSelect (RS) bit
	Define LCD_RSBIT 6	' Port Pin for RS bit
	Define LCD_EREG PORTA	' Port for Enable (E) bit
	Define LCD_EBIT 7	' Port Pin for E bit
	Define LCB_BITS 4	' Using 4-bit bus
	Define LCD_LINES 2	' Using 2 line Display
	Define LCD_COMMANDUS 2000
				' Command Delay (uS)
	Define LCD_DATAUS 50	' Data Delay (uS)
		'
		'	DS1307 Connections
		'	------------------
		'	10K Resistors required from PORTB.1 and PORTB.2 to Vdd
		'	PORTB.0 relies on weak pull-up's (for future use)
		'
	SQWpin var PORTB.0
		'	You don't actually need to wire this pin... but...
		'	If you're observant, you'll notice I've wired the DS1307's 
		'	SQW to RB0.  Rather than 'poll' and display the time/date
		'	as in this example code, you can have an interrupt
		'	on RB0 each second to do this.  In fact if you look at
		' 	my code for setting the DS1307 you'll see I've already
		'	programmed it for a 1 second 'Tick' for exactly this
		'	purpose...
	SCLpin var PORTB.1
	SDApin var PORTB.2
		'
		'	Setting Buttons
		'	---------------
		'	Buttons are connected between PORTB.4, 5 & 6 down to Vss
		'	No Resistors required - we're using weak pull-up's
		'
	DecButton var PORTB.4	' Press to Decrement Button
	SetButton var PORTB.5	' Press to Set/memorise Button
	IncButton var PORTB.6	' Press to Increment Button
		'
		'	Unused Ports
		'	------------	
		' RA4
		' RA5
		' RB3
		' RB7

	'
	'	EEPROM Locations
	'	----------------
	Data @0,74,97,110,70,101,98,77,97,114,65,112,114
		' Jan Feb Mar Apr
	Data 77,97,121,74,117,110,74,117,108,65,117,103
		' May Jun Jul Aug
	Data 83,101,112,79,99,116,78,111,118,68,101,99
		' Sep Oct Nov Dec
	Data 84,117,101,87,101,100,84,104,117,70,114,105
		' Tue Wed Thu Fri
	Data 83,97,116,83,117,110,77,111,110
		' Sat Sun Mon

	'
	'	RAM Assignments and Variables
	'	-----------------------------
	CounterA var byte	' General purpose Variable
	CounterB var byte	' General purpose Variable
	CounterC var byte	' General purpose Variable
	CounterD var byte	' General purpose Variable
	RTCSec var byte		' Seconds
	RTCMin var byte		' Minutes
	RTCHour var byte	' Hours
	RTCWDay var byte	' Weekday
	RTCDay var byte		' Day
	RTCMonth var byte	' Months
	RTCYear var byte	' Year
	RTCCtrl var byte	' Control 
	SetTime var byte	' 12/24 Hour Clock
	SetSec var byte		' Seconds
	SetMin var byte		' Minutes
	SetHour var byte	' Hours
	SetDay var byte		' Day
	SetMonth var byte	' Months
	SetYear var byte	' Year
	TimeOut var word	' Variable for SetUp Menu Time-Out

	'
	'	Program Constants
	'	-----------------
	ButtonRepeat con 200	' Timeperiod (in mS for Edit Buttons auto-repeat
				' should you want to hold them down...
	
	'
	'	Start Program
	'	=============
	goto JumpStart

	'
	'	Subroutine Nest Area
	'	====================

	'
	'	Subroutine Converts back to BCD
	'	-------------------------------
		' CounterA is the entry variable
		' CounterB holds the converted BCD result on exit
ConvertBCD:
	CounterB=CounterA DIG 1
	CounterB=CounterB<<4
	CounterB=CounterB+CounterA DIG 0
	Return

	'
	'	Subroutine Displays Month at current Cursor Position
	'	----------------------------------------------------
		' CounterB holds Month (1-12) this value is destroyed by the routine
		' additionally uses CounterA and CounterD variables
DisplayMonth:
	CounterB=CounterB*3-3	' Convert BCD Month to EEPROM Start Address
DisplaySequence:
	For CounterA=CounterB to CounterB+2
				' Read and Display Month
		Read CounterA,CounterD
		LCDOut CounterD
		Next CounterA
	Return

	'
	'	Subroutine works out Number of Days in the Month
	'	------------------------------------------------
		' SetYear - entry variable containing year (0-99)
		' SetMonth - entry Variable Containing Month (1-12)
		' CounterA - exits with number of days
FindDays:
	LookUp SetMonth-1,[31,28,31,30,31,30,31,31,30,31,30,31],CounterA
		' Above line gives the 'usual' days of the Month 
		' Section below adjusts for Leap-Year
	If SetMonth=2 then
		If (SetYear&$03)=0 then CounterA=29
		endif
	Return
	
	'
	'	Subroutine waits until user reseases Set Button
	'	-----------------------------------------------
SetButtonRelease:
	LCDOut $FE,1
	While SetButton=0:Wend
	Pause 250	' Small pause to kill any Key-Bounce
	Return

	'
	'	End of Subroutine Nest Area
	'	---------------------------

	'
	'	Actual Start of Real Program
	'	============================
JumpStart:
	'
	'	Set Hardware Directions
	'	-----------------------
	CMCON=%00000111		' Disable Comparators
	TRISA=%00000000		' PORTA all set to Output 
	TRISB=%11111111		' PORTB all set to Input
	OPTION_REG.7=0		' Enable Weak Pull-Ups
	'
	'	Reset Hardware
	'	--------------
	Pause 200		' Timeout for LCD to settle

	'
	'	Time & Date Display Loop
	'	========================
ReDisplay:
	LCDOut $FE,1		' Clear LCD
ReDisplayLoop:
		'
		'	Read RTC
		'	--------
	I2CRead SDApin,SCLpin,$D0,$00,[RTCSec,RTCMin,RTCHour,RTCWDay,RTCDay,RTCMonth,RTCYear,RTCCtrl]
		'
		'	Decide if Setup Required/Wanted
		'	-------------------------------
	If RTCSec.7=1 then goto SetUpPreset
				' Hands-Up who noticed the fact that when the DS1307
				' powers-up initially from cold, and all it's
				' registers are potentially "unpredictable", that 
				' Seconds bit 7 is always high?
				' We can use this feature to automatically jump to
				' our 'Setup' routine on initial power-up if the user
				' forgets to keep 'Setup' depressed and so prevent
				' any 'undesriable' Time & Date displays...
	If SetButton=0 then
				' If Set Button is pressed, User will be taken to
				' the Setup Menu.  If the Set Button is pressed at
				' Power-Up, then the User will be taken to the Setup
				' Menu directly without any intermediate display
		Gosub SetButtonRelease
		goto Setup
		endif	
	'
	'	Display RTC
	'	-----------
	'	Displayed on 2-line 16 character LCD in the form...
	'		10:15:00 AM
	'		Wed 12 Jun 2002
	'	Note - AM/PM indicator only shows in 12 hour mode.
	'	Additionally there is Hours leading Zero Surpression in 12-Hour Mode
	'	   after-all, you don't display 07pm do you?
	'
		'
		'	Display Time on Line 1
		'	----------------------
	LCDOut $FE,$80
	If RTCHour.6=1 then
			' Work-Out 12 or 24 hour Display for Hours
		CounterA=(RTCHour>>4)&$01
		else
		CounterA=(RTCHour>>4)&$03
		endif
	CounterA=CounterA*10+(RTCHour&$0F)
	If RTCHour.6=1 then
			' Display Hours appropriately for 12 or 24 hour Mode 
		LCDOut #CounterA
		else
		LCDOut #CounterA Dig 1,#CounterA Dig 0
		endif
	LCDOut ":",#(RTCMin>>4)&$0F,#RTCMin&$0F,":"
	LCDOut #(RTCSec>>4)&$0F,#RTCSec&$0F," "
	IF RTCHour.6=1 then
		If RTCHour.5=1 then
			LCDOut "PM"
			else
			LCDOut "AM"
			endif
		endif
		'
		'	Display Day of Week on Line 2
		'	-----------------------------
			' Let's face it... the DS1307 doesn't calculate the Day of Week,
			' all it does is increment a number at each mignight from
			' 1 thru 7 and roll back to start from 1 all over again.
			' It's really up to you to work out the DOW and program it in.
			' This is a tough nut for integer math to do... see the
			' 'Calculate Day of Week' routine in the setup section.
	LCDOut " ",$FE,$C0
	CounterB=RTCWDay*3+33	' Convert BCD WeekDay to EEPROM Start Address
	Gosub DisplaySequence	' Display 3 character sequence from EEPROM
		'
		'	Display Date on Line 2
		'	----------------------
	LCDOut " ",#(RTCDay>>4)&$0F,#RTCDay&$0F," "
	CounterB=((RTCMonth>>4)&$0F)*10+(RTCMonth&$0F)
	Gosub DisplayMonth
	LCDOut " 20",#(RTCYear>>4)&$0F,#RTCYear&$0F,$FE,$80
	'
	Pause 250		' Repeat about 4 times/sec
	Goto ReDisplayLoop

	'
	'	Clock and Calandar Settings
	'	===========================
		'
		'	Initial Default Settings
		'	------------------------
		' Default setting preset to the moment this program first born
		'
SetupPreset:
	RTCSec=$00		' Seconds preset to 00
	RTCMin=$15		' Minutes preset to 15
	RTCHour=$13		' Hours preset to 13'00
	RTCWDay=$01		' Weekday preset to 01
	RTCDay=$12		' Day preset 12
	RTCMonth=$06		' Months preset to June
	RTCYear=$02		' Year preset to 2002
	RTCCtrl=$10		' Control preset to output 1 second 'Tick' on SQWpin 
		'
		'	Convert the Current BCD Data to Something easier to Edit
		'	--------------------------------------------------------
Setup:
	SetTime=RTCHour.6	' Determines 12/24 Hour mode
	If SetTime=1 then
				' Regardless of 12/24 options, SetHour always
				' contains the hours in 24 hour clock format
				' otherwise there's twice the amount of work
				' to keep track in the edit routines
		SetHour=(RTCHour>>4)&$01
				' if 12-hour mode
		else
		SetHour=(RTCHour>>4)&$03
				' if 24 hour mode
		endif
	SetHour=SetHour*10+(RTCHour&$0F)
	If SetTime=1 then
		If RTCHour.5=1 then
			If SetHour<12 then SetHour=SetHour+12
				' Add-in the AM/PM indicator
			else
			If SetHour=12 then SetHour=0
			endif
		endif
	SetMin=((RTCMin>>4)&$0F)*10+(RTCMin&$0F)
	SetSec=((RTCSec>>4)&$0F)*10+(RTCSec&$0F)
	SetYear=((RTCYear>>4)&$0F)*10+(RTCYear&$0F)
	SetMonth=((RTCMonth>>4)&$0F)*10+(RTCMonth&$0F)
	SetDay=((RTCDay>>4)&$0F)*10+(RTCDay&$0F)
		'
		'	Setup Menu Loop
		'	---------------
	CounterC=0		' Set to Start of Setup (seven steps from 0 to 6)
	TimeOut=0		' Zero the Time-Out counter
SetupLoop:
	LCDOut $FE,1,"Set "
		'
		'	First Display the Menu Prompts
		'	------------------------------
		'	This is a crude way of doing the entry Menu's but it was
		'	pretty fast and quite mindless to program this lunch-hour
		'
	If CounterC=0 then
		LCDOut "Mode"
		endif
	If CounterC=1 then 
		LCDOut "Hours"
		endif
	If CounterC=2 then
		LCDOut "Minutes"
		endif
	If CounterC=3 then 
		LCDOut "Seconds"
		endif
	If CounterC=4 then
		LCDOut "Year : 20"
		endif
	If CounterC=5 then
		LCDOut "Month"
		endif
	If CounterC=6 then
		LCDOut "Day"
		endif
	If CounterC<>4 then LCDOut " :"
		'
		'	Now Display the Data
		'	--------------------
SetupDisplayLoop:
			'
			' 	12/24 Hour Option
			'	-----------------
	If CounterC=0 then
		LCDOut $FE,$8B
		If SetTime=0 then
			LCDOut "24HR"
			else 
			LCDOut "12HR"
			endif
		LCDOut $FE,$8B
		endif
			'	Hours
			'	-----
			'	Hours are tricky because of the 12/24 & AM/PM bit
			'	so we have to reconvert 1-24 back to 1-12 plus AM/PM
	If CounterC=1 then
		CounterA=SetHour
		If SetTime=1 then
			LCDOut $FE,$8E
				' Display AM/PM bit
			If CounterA<12 then
				LCDOut "AM"
				else
				LCDOut "PM"
				endif
			If CounterA=0 then CounterA=12
			If CounterA>12 then CounterA=CounterA-12
			endif
		LCDOut $FE,$8C,#CounterA
		If CounterA<10 then LCDOut " "			
		LCDOut $FE,$8C
		endif
			'
			'	Minutes
			'	-------
	If CounterC=2 then
		LCDOut $FE,$8E,#SetMin
		If SetMin<10 then LCDOut " "
		LCDOut $FE,$8E
		endif
			'
			'	Seconds
			'	-------
	If CounterC=3 then
		LCDOut $FE,$8E,#SetSec
		If SetSec<10 then LCDOut " "
		LCDOut $FE,$8E
		endif
			'
			'	Year
			'	----
	If CounterC=4 then
		LCDOut $FE,$8D,#SetYear DIG 1,#SetYear Dig 0
		LCDOut $FE,$8D
		endif
			'
			'	Month
			'	-----
			'	Here's a nice touch to setting the month rather
			'	than the usual number entry method...
			'
	If CounterC=5 then
		LCDOut $FE,$8C
		CounterB=SetMonth
		Gosub DisplayMonth
		LCDOut $FE,$8C
		endif
			'
			'	Day
			'	---
	If CounterC=6 then
		LCDOut $FE,$8A,#SetDay
		If SetDay<10 then LCDOut " "
		LCDOut $FE,$8A
		endif
		'
		'	The Data-Entry Bit
		'	------------------
SetupEntryLoop:
			'
			'	Decrement Button Pressed
			'	------------------------
	If DecButton=0 then
				'
				'	12/24 Clock Selection
				'	---------------------
		If CounterC=0 then
			If SetTime=0 then 
				SetTime=1
				else
				SetTime=0
				endif
			endif
				'
				'	Decrement Hours
				'	---------------
		If CounterC=1 then
			If SetHour=0 then
				SetHour=23
				else
				SetHour=SetHour-1
				endif
			endif
				'
				'	Decrement Minutes
				'	-----------------
		If CounterC=2 then
			If SetMin=0 then
				SetMin=59
				else
				SetMin=SetMin-1
				endif
			endif
				'
				'	Decrement Seconds
				'	-----------------
				'	Kinda overkill to include setting seconds,
				'	but if you got codespace to burn...
		If CounterC=3 then
			If SetSec=0 then
				SetSec=59
				else
				SetSec=SetSec-1
				endif
			endif
				'
				'	Decrement Years
				'	---------------
		If CounterC=4 then
			If SetYear=0 then
				SetYear=99
				else
				SetYear=SetYear-1
				endif
			endif
				'
				'	Decrement Month
				'	---------------
		If CounterC=5 then
			If SetMonth=1 then
				SetMonth=12
				else
				SetMonth=SetMonth-1
				endif
					'
					' Changing the Month has a bearing on the
					' days, especially if you've got days preset
					' to 31 and you now select a month with fewer
					' days... so this bit here just prevents that
			Gosub FindDays
			If SetDay>CounterA then SetDay=CounterA
			endif
				'
				'	Decrement Days
				'	--------------
				'	Tricky one here... go work-out the days
				'	of the month first...
		If CounterC=6 then
			Gosub FindDays
			If SetDay=1 then
				SetDay=CounterA
				else
				SetDay=SetDay-1
				endif
			endif
		Pause ButtonRepeat
		TimeOut=0
		Goto SetUpDisplayLoop
		endif
			'
			'	Increment Button Pressed
			'	------------------------
			' 	This is just like Decrement but opposite...
	If IncButton=0 then
				'
				'	12/24 Clock Selection
				'	---------------------
		If CounterC=0 then
			If SetTime=1 then 
				SetTime=0
				else
				SetTime=1
				endif
			endif
				'
				'	Increment Hours
				'	---------------
		If CounterC=1 then
			If SetHour=23 then
				SetHour=0
				else
				SetHour=SetHour+1
				endif
			endif
				'
				'	Increment Minutes
				'	-----------------
		If CounterC=2 then
			If SetMin=59 then
				SetMin=0
				else
				SetMin=SetMin+1
				endif
			endif
				'
				'	Increment Seconds
				'	-----------------
				'	Kinda overkill to include setting seconds,
				'	but if you got codespace to burn...
		If CounterC=3 then
			If SetSec=59 then
				SetSec=0
				else
				SetSec=SetSec+1
				endif
			endif
				'
				'	Increment Years
				'	---------------
		If CounterC=4 then
			If SetYear=99 then
				SetYear=0
				else
				SetYear=SetYear+1
				endif
			endif
				'
				'	Increment Month
				'	---------------
		If CounterC=5 then
			If SetMonth=12 then
				SetMonth=1
				else
				SetMonth=SetMonth+1
				endif
					'
					' Changing the Month has a bearing on the
					' days, especially if you've got days preset
					' to 31 and you now select a month with fewer
					' days... so this bit here just prevents that
			Gosub FindDays
			If SetDay>CounterA then SetDay=CounterA
			endif
				'
				'	Increment Days
				'	--------------
				'	Tricky one here... go work-out the days
				'	of the month first...
		If CounterC=6 then
			Gosub FindDays
			If SetDay=>CounterA then
				SetDay=1
				else
				SetDay=SetDay+1
				endif
			endif
		Pause ButtonRepeat
		TimeOut=0
		Goto SetupDisplayLoop
		endif
			'
			'	Set Button Pressed
			'	------------------
	If SetButton=0 then
		CounterC=CounterC+1
				' Increment Menu Item
		TimeOut=0
		If CounterC>6 then
				' Save Data if all edit items exhaused
			LCDOut $FE,1,"Memorised"
				' Make the User feel happy
				'
				'	Save 12/24 Hours to BCD DS1307's Format
				'	---------------------------------------
			CounterA=SetHour
			If SetTime=1 then
				If CounterA>12 then CounterA=CounterA-12
				If CounterA=0 then CounterA=12
				endif
			Gosub ConvertBCD
			RTCHour=CounterB
					' Save the Hours Value
			If SetTime=1 then
				RTCHour.6=1
					' Save the 12 Hour Mode Flag
				If SetHour=>12 then RTCHour.5=1
					' Save the 'PM' Flag
				endif
				'
				'	Save Minutes
				'	------------
			CounterA=SetMin
			Gosub ConvertBCD
			RTCMin=CounterB
				'
				'	Save Seconds
				'	------------
			CounterA=SetSec
			Gosub ConvertBCD
			RTCSec=CounterB
				'
				'	Save Year
				'	---------
			CounterA=SetYear
			Gosub ConvertBCD
			RTCYear=CounterB
				'
				'	Save Month
				'	----------
			CounterA=SetMonth
			Gosub ConvertBCD
			RTCMonth=CounterB
				'
				'	Save Day
				'	--------
			CounterA=SetDay
			Gosub ConvertBCD
			RTCDay=CounterB
				'
				'	Calculate Day of Week & Save
				'	----------------------------
					' Melanie's fudge for calculating Days of Week
					' using PBP's integer math...
					' by the time someone's clock displays the
					' the wrong Day of Week, I'll hopefully have long
					' retired and be past caring...
					' I wouldn't however go stick this routine in a 
					' Nuclear Power Station to purge the reactor
					' every Monday morning without checking how far it
					' will work before the integer math overflows...
					' In my routine RTCWDay=1 is a Tuesday (the
					' start of my week) and continues sequentially
					' until RTCWDay=7 which is a Monday
			CounterA=SetYear+4
			CounterB=SetMonth
			If SetMonth<3 then
				CounterA=CounterA-1
				CounterB=CounterB+12
				endif
			CounterD=(SetDay+(153*CounterB-457)/5+365*CounterA+CounterA/4-CounterA/100+CounterA/400+2) MOD 7
			RTCWDay=CounterD+1
				'
				'	Do the Business
				'	---------------

			I2CWrite SDApin,SCLpin,$D0,$00,[RTCSec,RTCMin,RTCHour,RTCWDay,RTCDay,RTCMonth,RTCYear,RTCCtrl]
			Pause 1000
			Gosub SetButtonRelease
			Goto ReDisplay
			endif
		Gosub SetButtonRelease
		Goto SetupLoop	' Loop for Next Menu Item
		endif
		'
		'	Menu TimeOut Counter
		'	--------------------
	Pause 1			' Kill 1mS 
	TimeOut=TimeOut+1
	If TimeOut>20000 then goto ReDisplay
				' User paused too long
				' Return User to Time & Date Display
				' This will however bounce the User to the original
				' Setup Menu start point if the DS1307 has not 
				' been initialised
	Goto SetupEntryLoop	' Loop for Button Press

	'
	End




